package com.fubowen.lepton.helper

import android.animation.ObjectAnimator
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.res.ColorStateList
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.Rect
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.GradientDrawable
import android.net.Uri
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.TypedValue
import android.view.Gravity
import android.view.LayoutInflater
import android.view.PixelCopy
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.view.WindowManager
import android.widget.ImageView
import android.widget.PopupWindow
import android.widget.TextView
import androidx.annotation.StringRes
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.animation.doOnEnd
import androidx.core.animation.doOnStart
import androidx.core.view.WindowCompat
import androidx.core.widget.ImageViewCompat
import androidx.fragment.app.FragmentContainerView
import com.fubowen.lepton.R
import com.fubowen.lepton.conf.AnnotationConfig
import com.fubowen.lepton.databinding.LeptonConfirmBinding
import com.fubowen.lepton.databinding.LeptonPasswordBinding
import com.fubowen.lepton.databinding.LeptonPromptBinding
import com.fubowen.lepton.databinding.LeptonToastBinding
import com.fubowen.lepton.extension.digest
import com.google.android.material.bottomsheet.BottomSheetDialog
import java.io.File
import kotlin.math.roundToInt

object ViewHelper {

    //region View 扩展函数
    @JvmStatic
    fun View.setVisible(visible: Boolean) {
        visibility = if (visible) View.VISIBLE else View.GONE
    }

    @JvmStatic
    fun View.dpToPx(dp: Int): Int {
        return this.context.dpToPx(dp)
    }

    @JvmStatic
    fun View.createBitmapFromView(window: Window, callback: (Bitmap, Boolean) -> Unit) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val bitmap = Bitmap.createBitmap(this.width, this.height, Bitmap.Config.ARGB_8888)
            val point = IntArray(2)
            this.getLocationInWindow(point)
            val (x, y) = point
            val region = Rect(x, y, x + this.width, y + this.height)
            val finishCallback = PixelCopy.OnPixelCopyFinishedListener {
                if (it == PixelCopy.SUCCESS) {
                    callback(bitmap, true)
                } else {
                    callback(bitmap, false)
                }
            }
            PixelCopy.request(
                window,
                region,
                bitmap,
                finishCallback,
                Handler(Looper.getMainLooper())
            )
        } else {
            this.isDrawingCacheEnabled = true
            this.buildDrawingCache()
            val bitmap = this.drawingCache
            callback(bitmap, true)
            this.destroyDrawingCache()
        }
    }

    @JvmStatic
    fun View.findViewsByTag(tag: String): List<View> {
        val mutableList = mutableListOf<View>()
        if (this.tag == tag) {
            mutableList.add(this)
        }
        if (this is ViewGroup) {
            val count = this.childCount
            for (i in 0 until count) {
                val childView = this.getChildAt(i)
                mutableList.addAll(childView.findViewsByTag(tag))
            }
        }
        return mutableList
    }

    @JvmStatic
    fun View.toast(message: CharSequence, ms: Long?) {
        val binding = LeptonToastBinding.inflate(LayoutInflater.from(this.context))
        binding.toastText.text = message
        val popup = PopupWindow().apply {
            contentView = binding.root
            width = ViewGroup.LayoutParams.MATCH_PARENT
            height = ViewGroup.LayoutParams.WRAP_CONTENT
            isOutsideTouchable = true
            isTouchable = true
            isFocusable = false
            elevation = 100f
        }
        ObjectAnimator.ofInt(0, 1).let {
            it.duration = ms ?: 3000
            it.doOnEnd {
                popup.dismiss()
            }
            it.doOnStart {
                val point = IntArray(2)
                this.getLocationInWindow(point)
                val y = point[1] + this.height * 0.1
                popup.showAtLocation(this, Gravity.TOP, 0, y.toInt())
            }
            it.start()
        }
    }

    @JvmStatic
    fun View.spToPx(sp: Float) =
        TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, this.resources.displayMetrics)

    /**
     * GradientDrawable.RECTANGLE：矩形形状
     * GradientDrawable.OVAL：椭圆形状
     * GradientDrawable.LINE：线形状
     * GradientDrawable.RING：环形状
     */
    @JvmStatic
    fun View.setShapeColor(color: Int, shape: Int = GradientDrawable.RECTANGLE, radius: Int = 8) {
        val shapeDrawable = GradientDrawable()
        shapeDrawable.shape = shape
        shapeDrawable.cornerRadius = dpToPx(radius).toFloat()
        shapeDrawable.setColor(color)
        this.background = shapeDrawable
    }

    //region setThemeColor
    @JvmStatic
    fun View.setThemeColor(newColor: String) {
        if (newColor.isNullOrBlank()) {
            return
        }
        val colorInt = Color.parseColor(newColor)
        this.setThemeColor(colorInt)
    }

    @JvmStatic
    fun View.setThemeColor(newColor: Int) {
        val hasTheme = tag == null || !tag.toString().contains("noTheme")
        if (hasTheme) {
            when (val bg = background) {
                is ColorDrawable -> setBackgroundColor(newColor)
                is GradientDrawable -> bg.setColor(newColor)
                else -> setBackgroundColor(newColor)
            }
        }
        if (this is ViewGroup) {
            for (i in 0 until childCount) {
                val child = getChildAt(i)
                child.setThemeColor(newColor)
            }
        }
    }

    @JvmStatic
    fun View.setThemeColor(bgColor: Int, textColor: Int) {
        val hasTheme = tag == null || !tag.toString().contains("noTheme")
        if (hasTheme) {
            when (val bg = background) {
                is ColorDrawable -> setBackgroundColor(bgColor)
                is GradientDrawable -> bg.setColor(bgColor)
                else -> setBackgroundColor(bgColor)
            }
            when (this) {
                is TextView -> this.setTextColor(textColor)
                is ImageView -> ImageViewCompat.setImageTintList(
                    this,
                    ColorStateList.valueOf(textColor)
                )
            }
        }
        if (this is ViewGroup) {
            for (i in 0 until childCount) {
                val child = getChildAt(i)
                child.setThemeColor(bgColor, textColor)
            }
        }
    }
    //endregion

    //endregion

    //region ConstraintLayout 扩展函数
    fun ConstraintLayout.setConnect(
        startID: Int,
        startSide: Int,
        endID: Int,
        endSide: Int,
        margin: Int,
        vararg clearIds: Int
    ) {
        val set = ConstraintSet()
        set.clone(this)
        clearIds.forEach {
            set.clear(startID, it)
        }
        set.connect(
            startID,
            startSide,
            endID,
            endSide,
            margin
        )
        set.applyTo(this)
    }
    //endregion

    //region Window 扩展函数
    fun Window.setFullScreen() {
        WindowCompat.setDecorFitsSystemWindows(this, false)
    }
    //endregion

    //region ViewGroup 扩展函数
    fun ViewGroup.toast(@StringRes resId: Int, ms: Long?) {
        val text = this.resources.getText(resId)
        this.toast(text, ms)
    }

    fun ViewGroup.toast(message: CharSequence, ms: Long?) {
        val binding = LeptonToastBinding.inflate(LayoutInflater.from(this.context))
        binding.toastText.text = message
        val popup = PopupWindow().apply {
            contentView = binding.root
            width = ViewGroup.LayoutParams.MATCH_PARENT
            height = ViewGroup.LayoutParams.WRAP_CONTENT
            isOutsideTouchable = true
            isTouchable = true
            isFocusable = false
            elevation = 100f
        }
        ObjectAnimator.ofInt(0, 1).let {
            it.duration = ms ?: 3000
            it.doOnEnd {
                popup.dismiss()
            }
            it.doOnStart {
                val point = IntArray(2)
                this.getLocationInWindow(point)
                val y = point[1] + this.height * 0.1
                popup.showAtLocation(this, Gravity.TOP, 0, y.toInt())
            }
            it.start()
        }
    }
    //endregion

    //region ImageView 扩展函数
    fun ImageView.setIcon(icon: String?, iconSource: Int) {

        if (icon.isNullOrBlank()) {
            return
        }
        when (iconSource) {

            AnnotationConfig.ICON_SOURCE_URL -> {
                val file = Uri.fromFile(File(icon))
                this.setImageURI(file)
            }

            AnnotationConfig.ICON_SOURCE_RESOURCE -> {
                val id = icon.toInt()
                this.setImageResource(id)
            }

            AnnotationConfig.ICON_SOURCE_STRING -> {}
        }
    }
    //endregion

    //region Context 扩展函数
    @JvmStatic
    fun Context.createPopupWindow(onCreateView: (LayoutInflater, PopupWindow) -> View) = lazy {
        val inflater = LayoutInflater.from(this)
        PopupWindow().apply {
            width = ViewGroup.LayoutParams.MATCH_PARENT
            height = ViewGroup.LayoutParams.WRAP_CONTENT
            isTouchable = true
            elevation = 10f
            contentView = onCreateView(inflater, this)
        }
    }

    @JvmStatic
    fun Context.popupWindow(onCreateView: (LayoutInflater, PopupWindow) -> View): PopupWindow {
        val inflater = LayoutInflater.from(this)
        return PopupWindow().apply {
            width = ViewGroup.LayoutParams.MATCH_PARENT
            height = ViewGroup.LayoutParams.WRAP_CONTENT
            isTouchable = true
            contentView = onCreateView(inflater, this)
        }
    }

    fun Context.confirm(title: String, ok: () -> Unit) {
        BottomSheetDialog(this).apply {
            val content = LeptonConfirmBinding.inflate(layoutInflater)
            content.confirmTitle.text = title
            content.confirmSubmit.setOnClickListener { ok(); hide() }
            content.confirmCancel.setOnClickListener { hide() }
            setContentView(content.root)
            show()
        }
    }

    fun Context.confirm(@StringRes resId: Int, ok: () -> Unit) {
        val text = resources.getString(resId)
        confirm(text, ok)
    }

    fun Context.confirm(@StringRes resId: Int, vararg args: Any, ok: () -> Unit) {
        var text = resources.getString(resId)
        text = String.format(text, *args)
        confirm(text, ok)
    }

    fun Context.prompt(@StringRes resId: Int, ok: (String) -> Pair<Boolean, Int>) {
        val text = resources.getString(resId)
        prompt(text, ok)
    }

    fun Context.prompt(
        @StringRes resId: Int,
        vararg args: Any,
        ok: (String) -> Pair<Boolean, Int>
    ) {
        var text = resources.getString(resId)
        text = String.format(text, *args)
        prompt(text, ok)
    }

    fun Context.prompt(title: String, ok: (String) -> Pair<Boolean, Int>) {
        BottomSheetDialog(this).apply {
            val content = LeptonPromptBinding.inflate(layoutInflater)
            content.promptTitle.text = title
            content.promptSubmit.setOnClickListener {
                val text = content.promptInput.text.toString()
                val (isSuccess, msgId) = ok(text)
                if (isSuccess) {
                    hide()
                } else {
                    if (msgId != 0) {
                        val text = resources.getString(msgId)
                        content.promptInput.error = text
                    }
                }
            }
            content.promptCancel.setOnClickListener { hide() }
            setContentView(content.root)
            show()
        }
    }

    fun Context.password(
        resId: Int,
        pwd: String?,
        ok: (String, String) -> Pair<Boolean, Int>
    ) {
        BottomSheetDialog(this).apply {
            val binding = LeptonPasswordBinding.inflate(layoutInflater)
            val title = resources.getString(resId)
            binding.passwordTitle.text = title
            binding.passwordSubmit.setOnClickListener {
                val old = binding.passwordOld.text.toString()
                if (old.isBlank()) {
                    val msg = resources.getString(R.string.password_old_empty_tip)
                    binding.passwordOld.error = msg
                    return@setOnClickListener
                }
                if (old.digest() != pwd) {
                    val msg = resources.getString(R.string.password_not_equal_tip)
                    binding.passwordOld.error = msg
                    return@setOnClickListener
                }
                val new = binding.passwordNew.text.toString()
                if (new.isBlank()) {
                    val msg = resources.getString(R.string.password_new_empty_tip)
                    binding.passwordNew.error = msg
                    return@setOnClickListener
                }
                val result = ok(new, old)
                if (result.first) {
                    hide()
                    return@setOnClickListener
                }
                if (result.second != 0) {
                    val msg = resources.getString(result.second)
                    binding.passwordNew.error = msg
                }
            }
            binding.passwordCancel.setOnClickListener { hide() }
            setContentView(binding.root)
            setOnShowListener {
                window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
            }
            show()
        }
    }

    fun Context.writeTextToClipboard(title: String?, text: String?) {
        val clipboard = this.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
        val clip = ClipData.newPlainText(title, text)
        clipboard.setPrimaryClip(clip)
    }

    /**
     * 获取当前是否为深色模式
     * 深色模式的值为:0x21
     * 浅色模式的值为:0x11
     * @return true 深色模式   false 不是深色模式
     */
    fun Context.isNight(): Boolean {
        return resources.configuration.uiMode == Configuration.UI_MODE_NIGHT_YES
    }

    fun Context.createFragmentContainerView(): FragmentContainerView {
        val container = FragmentContainerView(this)
        container.id = View.generateViewId()
        container.layoutParams = ViewGroup.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.MATCH_PARENT
        )
        return container
    }

    @JvmStatic
    fun Context.pxToDp(px: Int): Int {
        val scale = this.resources.displayMetrics.density
        return (px / scale).roundToInt()
    }

    @JvmStatic
    fun Context.dpToPx(dp: Int): Int {
        val scale = this.resources.displayMetrics.density
        return (dp * scale).roundToInt()
    }
    //endregion
}